package com.acme.exchange.network.server;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.BlockingQueue;
import com.acme.exchange.network.MessageTransceiver;
import com.acme.exchange.network.proto.DefinedMessage;
import com.acme.exchange.network.proto.Protocol.ExchangeMessage;

public class Server implements Runnable {
	private ServerSocketChannel ssc;
	private ServerSocket ss;
	private InetSocketAddress address;
	private Selector selector;
	private final Set<SelectionKey> keys;
	private boolean serverIsStoped;
	private byte[] clientOrder;
	private ExchangeMessage clientMessage;
	private MessageTransceiver mt;
	private final BlockingQueue<DefinedMessage> clientMessages;

	public Server(BlockingQueue<DefinedMessage> queue, Set<SelectionKey> keys) {
		clientMessages = queue;
		serverIsStoped = false;
		mt = new MessageTransceiver();
		this.keys = keys;
		try{
			selector = Selector.open();
			ssc = ServerSocketChannel.open();
			ss = ssc.socket();
			address = new InetSocketAddress(1027);
			ss.bind(address);
			
			ssc.configureBlocking(false);
			ssc.register(selector, SelectionKey.OP_ACCEPT);
		}catch (IOException e){
			
		}
	}

	public void run() {
		try {

			while (!serverIsStoped) {
				// wait for incoming events (new connections or orders)
				selector.select();

				Set<SelectionKey> selectedKeys = selector.selectedKeys();
				Iterator<SelectionKey> iter = selectedKeys.iterator();

				SelectionKey skey;

				while (iter.hasNext()) {
					skey = iter.next();
					if (skey.isAcceptable()) { // get a new connection

						ssc = (ServerSocketChannel) skey.channel();
						SocketChannel sc = ssc.accept();						
						// register a new connection with selector
						sc.configureBlocking(false);
						sc.register(selector, SelectionKey.OP_READ);

						//keys.add(newKey); //will add after checking of login
						iter.remove();

					} else if (skey.isReadable()) { // get new message

						SocketChannel sc;
						try {
							sc = (SocketChannel) skey.channel();
							synchronized (sc) {
								mt.receive(sc);								
							}

							while (mt.hasMessage()) {

								clientOrder = mt.nextMessage();
								
								clientMessage = ExchangeMessage.parseFrom(clientOrder);
								DefinedMessage dm = new DefinedMessage();
								dm.setKey(skey);
								dm.setMessage(clientMessage);
								//add client's message in queue to process
								clientMessages.put(dm);
							}
						} catch (IOException e) { // unset connection
							skey.cancel();
							keys.remove(skey);
						}
						iter.remove();
					}
				}
			}
			ss.close();
			ssc.close();
			selector.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}
